package org.example.mq.mqclient;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.example.mq.common.BasicArguments;
import org.example.mq.common.BasicReturns;
import org.example.mq.common.Request;
import org.example.mq.common.Response;
import org.example.mq.common.arguments.*;
import org.example.mq.common.exception.MqException;
import org.example.mq.common.tools.BinaryTool;
import org.example.mq.mqserver.core.BasicProperties;
import org.example.mq.mqserver.core.Consumer;
import org.example.mq.mqserver.core.ExchangeType;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Getter
@Slf4j
public class Channel {
    private final String channelId;
    private final Connection connection;
    private boolean isClose;
    private ConcurrentHashMap<String, BasicReturns>basicReturnsMap  = new ConcurrentHashMap<>();

    //一个channel订阅一个队列。所以一个channel一个回调函数
    private Consumer consumer = null;

    public Channel(String channelId, Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
        this.isClose = false;
    }

    //构造请求rid
    public String generateRid(){
        return "R-"+ UUID.randomUUID().toString();
    }

    //通知服务器创建channel
    public boolean createChannel() throws IOException {
        BasicArguments basicArguments = new BasicArguments();

        basicArguments.setChannelId(channelId);
        basicArguments.setRid(generateRid());
        basicArguments.setHostName(connection.getVirtualHost());

        byte[] payload = BinaryTool.toBytes(basicArguments);

        Request request =  new Request();
        request.setType(0x1);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        //等待服务器响应
        BasicReturns basicReturns = waitResult(basicArguments.getRid());

        return basicReturns.isOk();
    }

    public BasicReturns waitResult(String rid) {
        BasicReturns basicReturns = null;

        while((basicReturns = basicReturnsMap.get(rid))==null){
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        basicReturnsMap.remove(rid);

        return basicReturns;
    }

    public void putReturns(BasicReturns basicReturns){
        basicReturnsMap.put(basicReturns.getRid(), basicReturns);

        synchronized (this){
            notifyAll();
        }
    }

    //关闭channel
    public boolean close() throws IOException {
        Request request = new Request();
        BasicArguments basicArguments = new BasicArguments();

        basicArguments.setHostName(connection.getVirtualHost());
        basicArguments.setChannelId(channelId);
        basicArguments.setRid(generateRid());

        byte[] payload = BinaryTool.toBytes(basicArguments);

        request.setType(0x2);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns = waitResult(basicArguments.getRid());

        if(!isClose){
            isClose = basicReturns.isOk();
        }

        return basicReturns.isOk();
    }

    //创建交换机 0x3
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable, boolean autoDelete, Map<String,Object> arguments) throws IOException {
        Request request = new Request();
        ExchangeDeclareArguments exchangeDeclareArguments = new ExchangeDeclareArguments();

        exchangeDeclareArguments.setRid(generateRid());
        exchangeDeclareArguments.setChannelId(channelId);
        exchangeDeclareArguments.setHostName(connection.getVirtualHost());

        exchangeDeclareArguments.setExchangeName(exchangeName);
        exchangeDeclareArguments.setExchangeType(exchangeType);
        exchangeDeclareArguments.setDurable(durable);
        exchangeDeclareArguments.setAutoDelete(autoDelete);
        exchangeDeclareArguments.setArguments(arguments);

        byte[] payload = BinaryTool.toBytes(exchangeDeclareArguments);

        request.setType(0x3);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(exchangeDeclareArguments.getRid());
        return basicReturns.isOk();
    }

    //删除交换机
    public boolean deleteExchange(String exchange) throws IOException {
        Request request = new Request();
        DeleteExchangeArguments deleteExchangeArguments = new DeleteExchangeArguments();

        deleteExchangeArguments.setExchangeName(exchange);

        deleteExchangeArguments.setRid(generateRid());
        deleteExchangeArguments.setChannelId(channelId);
        deleteExchangeArguments.setHostName(connection.getVirtualHost());

        byte[] payload = BinaryTool.toBytes(deleteExchangeArguments);

        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(deleteExchangeArguments.getRid());
        return basicReturns.isOk();
    }

    //创建队列
    public boolean queueDeclare(String queueName,boolean durable,boolean exclusive,boolean autoDelete,Map<String,Object>arguments) throws IOException {
        Request request = new Request();
        QueueDeclareArguments queueDeclareArguments = new QueueDeclareArguments();

        queueDeclareArguments.setRid(generateRid());
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setHostName(connection.getVirtualHost());

        queueDeclareArguments.setQueueName(queueName);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setArguments(arguments);

        byte[] payload = BinaryTool.toBytes(queueDeclareArguments);

        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(queueDeclareArguments.getRid());

        return basicReturns.isOk();
    }

    //删除队列
    public boolean queueDelete(String queueName) throws IOException {
        Request request = new Request();
        DelQueueArguments delQueueArguments = new DelQueueArguments();

        delQueueArguments.setRid(generateRid());
        delQueueArguments.setChannelId(channelId);
        delQueueArguments.setHostName(connection.getVirtualHost());

        delQueueArguments.setQueueName(queueName);

        byte[] payload = BinaryTool.toBytes(delQueueArguments);

        request.setType(0x6);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(delQueueArguments.getRid());

        return basicReturns.isOk();
    }

    // 绑定队列到交换机
    public boolean queueBind(String exchangeName, String queueName, String bindingKey) throws IOException {
        Request request = new Request();
        QueueBindArguments queueBindArguments = new QueueBindArguments();

        queueBindArguments.setRid(generateRid());
        queueBindArguments.setChannelId(channelId);
        queueBindArguments.setHostName(connection.getVirtualHost());

        queueBindArguments.setExchangeName(exchangeName);
        queueBindArguments.setQueueName(queueName);
        queueBindArguments.setBindingKey(bindingKey);

        byte[] payload = BinaryTool.toBytes(queueBindArguments);

        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(queueBindArguments.getRid());
        return basicReturns.isOk();
    }

    // 解绑交换机
    public boolean exchangeUnBind(String exchangeName) throws IOException {
        Request request = new Request();
        ExchangeUnBindArguments exchangeUnBindArguments = new ExchangeUnBindArguments();

        exchangeUnBindArguments.setRid(generateRid());
        exchangeUnBindArguments.setChannelId(channelId);
        exchangeUnBindArguments.setHostName(connection.getVirtualHost());

        exchangeUnBindArguments.setExchangeName(exchangeName);

        byte[] payload = BinaryTool.toBytes(exchangeUnBindArguments);

        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(exchangeUnBindArguments.getRid());
        return basicReturns.isOk();
    }

    // 队列解绑交换机
    public boolean queueUnBind(String queueName, String exchangeName) throws IOException {
        Request request = new Request();
        QueueUnBindArguments queueUnBindArguments = new QueueUnBindArguments();

        queueUnBindArguments.setRid(generateRid());
        queueUnBindArguments.setChannelId(channelId);
        queueUnBindArguments.setHostName(connection.getVirtualHost());

        queueUnBindArguments.setQueueName(queueName);
        queueUnBindArguments.setExchangeName(exchangeName);

        byte[] payload = BinaryTool.toBytes(queueUnBindArguments);

        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(queueUnBindArguments.getRid());
        return basicReturns.isOk();
    }

    // 消息发布
    public boolean basicPublish(String exchangeName, String routingKey,
                                BasicProperties properties, byte[] body) throws IOException {
        Request request = new Request();
        BasicPublishArguments basicPublishArguments = new BasicPublishArguments();

        basicPublishArguments.setRid(generateRid());
        basicPublishArguments.setChannelId(channelId);
        basicPublishArguments.setHostName(connection.getVirtualHost());

        basicPublishArguments.setExchangeName(exchangeName);
        basicPublishArguments.setRoutingKey(routingKey);
        basicPublishArguments.setBasicProperties(properties);
        basicPublishArguments.setBody(body);

        byte[] payload = BinaryTool.toBytes(basicPublishArguments);

        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(basicPublishArguments.getRid());
        return basicReturns.isOk();
    }

    //订阅
    public boolean basicConsume(String consumeTag,String queueName,boolean autoAck,Consumer consumer) throws IOException {
        if(this.consumer!=null){
            throw new MqException("该channel已经设置过consumer！");
        }
        this.consumer = consumer;


        Request request = new Request();
        BasicConsumeArguments basicConsumeArguments = new BasicConsumeArguments();

        basicConsumeArguments.setRid(generateRid());
        basicConsumeArguments.setChannelId(channelId);
        basicConsumeArguments.setHostName(connection.getVirtualHost());

        basicConsumeArguments.setConsumeTag(consumeTag);
        basicConsumeArguments.setQueueName(queueName);
        basicConsumeArguments.setAutoAck(autoAck);

        byte[] payload = BinaryTool.toBytes(basicConsumeArguments);

        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(basicConsumeArguments.getRid());

        return basicReturns.isOk();
    }

    public boolean basicConsume(String queueName,boolean autoAck,Consumer consumer) throws IOException {
        if(this.consumer!=null){
            throw new MqException("该channel已经设置过consumer！");
        }
        this.consumer = consumer;


        Request request = new Request();
        BasicConsumeArguments basicConsumeArguments = new BasicConsumeArguments();

        basicConsumeArguments.setRid(generateRid());
        basicConsumeArguments.setChannelId(channelId);
        basicConsumeArguments.setHostName(connection.getVirtualHost());

        basicConsumeArguments.setQueueName(queueName);
        basicConsumeArguments.setAutoAck(autoAck);

        byte[] payload = BinaryTool.toBytes(basicConsumeArguments);

        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(basicConsumeArguments.getRid());

        return basicReturns.isOk();
    }


    //消息确认
    public boolean basicAck(String queueName,String messageId) throws IOException {
        Request request = new Request();
        BasicAckArguments basicAckArguments = new BasicAckArguments();

        basicAckArguments.setRid(generateRid());
        basicAckArguments.setHostName(connection.getVirtualHost());
        basicAckArguments.setChannelId(channelId);

        basicAckArguments.setMessageId(messageId);
        basicAckArguments.setQueueName(queueName);

        connection.writeRequest(request);

        BasicReturns basicReturns = waitResult(basicAckArguments.getRid());

        return basicReturns.isOk();
    }


}
